/*
 * Yabumi, a 2d scrolling shoot-em-up game.
 * Copyright (C) 2003 Gemma Teruaki <terugen@yabumi.com>
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.

 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
 */

#include <math.h>
#include <stdbool.h>
#include "../taskmanager.h"
#include "../sprite.h"
#include "../video.h"
#include "../util.h"
#include "missile.h"
#include "flame.h"
#include "homing.h"
#include "explosion.h"
#include "smoke.h"
#include "item.h"
#include "radar.h"
#include "hitcircle.h"
#include "enemy.h"

extern _list_header *active_task_list_header, *expired_task_list_header;
extern _list_header player_list_header;
_list_header enemy_list_header;

void Enemy_Init()
{
  Create_list(&enemy_list_header);
}

void Enemy_Free()
{
  Free_list(&enemy_list_header);
}

_Enemy *Enemy_Create(int x, int y, int dx, int dy, int angle)
{
  _list_item *list_item;
  list_item = (_list_item *)pmalloc(sizeof(_list_item));
  Add_item_last(&enemy_list_header, list_item);

  _Enemy *enemy = &list_item->data.Enemy;
  enemy->root = list_item;
  enemy->x = x;
  enemy->y = y;
  enemy->dx = dx;
  enemy->dy = dy;
  enemy->angle = angle;
  int i;
  for (i = 20; i < ENEMY_THRUST_TIME; i++) enemy->accel[i] = 100;
  for (i = 10; i < 20; i++) enemy->accel[i] = 80;
  for (i = 3; i < 10; i++) enemy->accel[i] = 60;
  for (i = 0; i < 3; i++) enemy->accel[i] = 80;
  enemy->shield = ENEMY_SHIELD;
  enemy->weapon = __weapon_missile;
  enemy->waits.fire = false;
  enemy->waits.thrust = false;
  enemy->tasks.fire = false;
  enemy->tasks.thrust = false;
  enemy->tasks.rotate_left = false;
  enemy->tasks.rotate_right = false;
  enemy->tasks.was_hit = __hit_no;
  return enemy;
}

task Enemy_Add(_option *optv)
{
  _Enemy *enemy;
  enemy = (_Enemy *)optv->socket[0];
  
  Task_Add(Enemy_Draw, enemy, NULL, INFINITY_COUNT);
  Task_Add(Hitcircle_Add, 
	   Hitcircle_Create(enemy, __hitcircle_enemy, 8, __visible, 0, 0),
	   enemy, ONETIME_COUNT);
  Task_Add(Enemy_Move, enemy, NULL, INFINITY_COUNT);
}

task Enemy_Move(_option *optv)
{
  _Enemy *enemy;
  enemy = (_Enemy *)optv->socket[0];

  if (enemy->shield <= 0) {
    optv->count = KILL_COUNT;
    Task_Add(Enemy_Kill, enemy, NULL, ENEMY_KILL_TIME);
    return;
  };

  if (enemy->dx > ENEMY_MAX_SPEED) enemy->dx = ENEMY_MAX_SPEED;
  if (enemy->dx < -1 * ENEMY_MAX_SPEED) enemy->dx = -1 * ENEMY_MAX_SPEED;
  if (enemy->dy > ENEMY_MAX_SPEED) enemy->dy = ENEMY_MAX_SPEED;
  if (enemy->dy < -1 * ENEMY_MAX_SPEED) enemy->dy = -1 * ENEMY_MAX_SPEED;
  cap_angle(&enemy->angle);
  enemy->x += enemy->dx >> 8;
  enemy->y += enemy->dy >> 8;

  if (enemy->tasks.fire == true) {
    enemy->tasks.fire = false;
    if (enemy->waits.fire != true) {
      enemy->waits.fire = true;
      Task_Add(Enemy_Fire, enemy, NULL, ONETIME_COUNT);
    };
  };
  if (enemy->tasks.thrust == true) {
    enemy->tasks.thrust = false;
    if (enemy->waits.thrust != true) {
      enemy->waits.thrust = true;
      Task_Add(Enemy_Thrust, enemy, NULL, ENEMY_THRUST_TIME);
    };
  };
  if (enemy->tasks.rotate_left == true) {
    enemy->tasks.rotate_left = false;
    Task_Add(Enemy_Rotate_Left, enemy, NULL, ENEMY_ROTATE_TIME);
  };
  if (enemy->tasks.rotate_right == true) {
    enemy->tasks.rotate_right = false;
    Task_Add(Enemy_Rotate_Right, enemy, NULL, ENEMY_ROTATE_TIME);
  };
  if (enemy->tasks.was_hit != __hit_no) {
    Task_Add(Enemy_was_Hit, enemy, NULL, ONETIME_COUNT);
  };

  /* AI Code Start */
  _list_item *list_item = player_list_header.next->next;
  if (list_item == player_list_header.prev) return;
  _Player *player = &list_item->data.Player;

  int tangle;
  if ((optv->count / -20) % 2 == 0) {
    enemy->tasks.thrust = true;
  } else {
    tangle = atan3(player->x - enemy->x, player->y - enemy->y) / RM_PI;
    if(ifclockwise(enemy->angle, tangle)) {
      enemy->tasks.rotate_right = true;
    } else {
      enemy->tasks.rotate_left = true;
    };
  };
  if (distance(enemy->x, enemy->y, player->x, player->y) < 300) {
    enemy->tasks.fire = true;
  };
  /* AI Code End */
}

task Enemy_Draw(_option *optv)
{
  _Enemy *enemy;
  enemy = (_Enemy *)optv->socket[0];
  /* Draw Enemy */
  int i;
  SDL_Surface *image;
  i = (optv->count / -10) % 3;
  cap_angle(&enemy->angle);
  image = Sprite_Enemy[i][enemy->angle];
  Sprite_Add(enemy->x, enemy->y, Z_ORDER_D2, image);
  
  if (enemy->shield <= ENEMY_SHIELD * 0.5) {
    Task_Add(Smoke_Draw, 
	     Smoke_Create(enemy->x, enemy->y),
	     NULL, ONETIME_COUNT + 3);
  };
  if (enemy->shield <= ENEMY_SHIELD * 0.3) {
    Task_Add(Smoke_Draw,
	     Smoke_Create(enemy->x, enemy->y + 10), 
	     NULL, ONETIME_COUNT + 7);
  };
}

task Enemy_Fire(_option *optv)
{
  _Enemy *enemy;
  enemy = (_Enemy *)optv->socket[0];

  switch (enemy->weapon) {
  case __weapon_missile:
    Task_Add(Enemy_Fire_Missile, enemy, NULL, ENEMY_FIRE_MISSILE_WAIT);
    break;
  case __weapon_flame:
    Task_Add(Enemy_Fire_Flame, enemy, NULL, ENEMY_FIRE_FLAME_WAIT);
    break;
  };
}

task Enemy_Fire_Missile(_option *optv)
{
  _Enemy *enemy;
  enemy = (_Enemy *)optv->socket[0];

  if (optv->count == ENEMY_FIRE_MISSILE_WAIT) {
    Task_Add(Missile_Add,
	     Missile_Create(enemy->x, enemy->y, enemy->dx, enemy->dy, enemy->angle),
	     NULL, ONETIME_COUNT);
  };
  if (optv->count == ONETIME_COUNT) enemy->waits.fire = false;
}

task Enemy_Fire_Flame(_option *optv)
{
  _Enemy *enemy;
  enemy = (_Enemy *)optv->socket[0];

  if (optv->count == ENEMY_FIRE_FLAME_WAIT) {
    Task_Add(Flame_Add, 
	     Flame_Create(enemy->x, enemy->y, enemy->dx, enemy->dy, enemy->angle), 
	     NULL, ONETIME_COUNT);
  };
  if (optv->count == ONETIME_COUNT) enemy->waits.fire = false;
}

task Enemy_Rotate_Left(_option *optv)
{
  _Enemy *enemy;
  enemy = (_Enemy *)optv->socket[0];

  enemy->angle += ENEMY_ROTATE_SPEED;
}

task Enemy_Rotate_Right(_option *optv)
{
  _Enemy *enemy;
  enemy = (_Enemy *)optv->socket[0];

  enemy->angle -= ENEMY_ROTATE_SPEED;
}

task Enemy_Thrust(_option *optv)
{
  _Enemy *enemy;
  enemy = (_Enemy *)optv->socket[0];
  
  enemy->dx += enemy->accel[optv->count] * cos(enemy->angle * RM_PI);
  enemy->dy += enemy->accel[optv->count] * sin(enemy->angle * RM_PI);
  
  int i;
  SDL_Surface *image;
  i = ((optv->count * 5) / ENEMY_THRUST_TIME) % 5;
  cap_angle(&enemy->angle);
  image = Sprite_Enemy_Thrust[i][enemy->angle];
  Sprite_Add(enemy->x, enemy->y, Z_ORDER_D2, image);

  if (optv->count == ENEMY_THRUST_WAIT) enemy->waits.thrust = false;
}

task Enemy_was_Hit(_option *optv)
{
  _Enemy *enemy;
  enemy = (_Enemy *)optv->socket[0];

  switch(enemy->tasks.was_hit) {
  case __hit_missile:
    Task_Add(Enemy_was_Hit_by_Missile, enemy, NULL, ONETIME_COUNT + 10);
    break;
  case __hit_flame:
    Task_Add(Enemy_was_Hit_by_Flame, enemy, NULL, ONETIME_COUNT + 10);
    break;
  case __hit_homing:
    Task_Add(Enemy_was_Hit_by_Homing, enemy, NULL, ONETIME_COUNT + 10);
    break;
  case __hit_item_missile:
    Task_Add(Enemy_was_Hit_by_Item_Missile, enemy, NULL, ONETIME_COUNT);
    break;
  case __hit_item_flame:
    Task_Add(Enemy_was_Hit_by_Item_Flame, enemy, NULL, ONETIME_COUNT +400);
    break;
  default:
    break;
  };
  enemy->tasks.was_hit = __hit_no;
}

task Enemy_was_Hit_by_Missile(_option *optv)
{
  _Enemy *enemy;
  enemy = (_Enemy *)optv->socket[0];
  
  if (optv->count % 2 == 0) {
    enemy->x += 3.0 * cos((enemy->angle + 90) * RM_PI);
    enemy->y += 3.0 * sin((enemy->angle + 90) * RM_PI);
  };
  if (optv->count % 2 == 1) {
    enemy->x += 3.0 * cos((enemy->angle - 90) * RM_PI);
    enemy->y += 3.0 * sin((enemy->angle - 90) * RM_PI);
  };

  if (optv->count == ONETIME_COUNT) {
    enemy->shield -= MISSILE_POWER;
  };
}

task Enemy_was_Hit_by_Flame(_option *optv)
{
  _Enemy *enemy;
  enemy = (_Enemy *)optv->socket[0];
  
  if (optv->count % 2 == 0) {
    enemy->x += 3.0 * cos((enemy->angle + 90) * RM_PI);
    enemy->y += 3.0 * sin((enemy->angle + 90) * RM_PI);
  };
  if (optv->count % 2 == 1) {
    enemy->x += 3.0 * cos((enemy->angle - 90) * RM_PI);
    enemy->y += 3.0 * sin((enemy->angle - 90) * RM_PI);
  };

  if (optv->count == ONETIME_COUNT) {
    enemy->shield -= FLAME_POWER;
  };
}

task Enemy_was_Hit_by_Homing(_option *optv)
{
  _Enemy *enemy;
  enemy = (_Enemy *)optv->socket[0];
  
  if (optv->count % 2 == 0) {
    enemy->x += 3.0 * cos((enemy->angle + 90) * RM_PI);
    enemy->y += 3.0 * sin((enemy->angle + 90) * RM_PI);
  };
  if (optv->count % 2 == 1) {
    enemy->x += 3.0 * cos((enemy->angle - 90) * RM_PI);
    enemy->y += 3.0 * sin((enemy->angle - 90) * RM_PI);
  };

  if (optv->count == ONETIME_COUNT) {
    enemy->shield -= HOMING_POWER;
  };
}

task Enemy_was_Hit_by_Item_Missile(_option *optv)
{
  _Enemy *enemy;
  enemy = (_Enemy *)optv->socket[0];
  
  if (optv->count == ONETIME_COUNT) {
    enemy->weapon = __weapon_missile;
  };
}

task Enemy_was_Hit_by_Item_Flame(_option *optv)
{
  _Enemy *enemy;
  enemy = (_Enemy *)optv->socket[0];
  
  if (optv->count > ONETIME_COUNT) {
    enemy->weapon = __weapon_flame;
  };
  if (optv->count == ONETIME_COUNT) {
    enemy->weapon = __weapon_missile;
  };
}

task Enemy_Kill(_option *optv)
{
  _Enemy *enemy;
  enemy = (_Enemy *)optv->socket[0];

  /* Move Enemy */
  enemy->x += enemy->dx >> 8;
  enemy->y += enemy->dy >> 8;
  enemy->dx *= 0.9;
  enemy->dy *= 0.9;
  cap_angle(&enemy->angle);

  if (optv->count == ONETIME_COUNT + 1) {
    Task_Add(Explosion_Draw, 
	     Explosion_Create(enemy->x, enemy->y), 
	     NULL, ONETIME_COUNT + 7);
    Task_Add(Item_Add,
	     Item_Create(enemy->x, enemy->y, 0, 0, __item_flame),
	     NULL, ONETIME_COUNT);
  };

  int i;
  _list_item *list_item;
  _task_item *task_item;

  if (optv->count == ONETIME_COUNT) {
    list_item = active_task_list_header->next->next;
    while (list_item != active_task_list_header->prev) {
      task_item = &list_item->data.task_item;
      for (i = 0; i < MAX_SOCKET; i++) {
	if (task_item->option.socket[i] == enemy) {
	  if (task_item->fp == Hitcircle_Move) {
	    task_item->option.count = ONETIME_COUNT;
	  } else {
	    task_item->option.count = KILL_COUNT;
	  };
	};
      };
      list_item = list_item->next;
    };
    list_item = expired_task_list_header->next->next;
    while (list_item != expired_task_list_header->prev) {
      task_item = &list_item->data.task_item;
      for (i = 0; i < MAX_SOCKET; i++) {
	if (task_item->option.socket[i] == enemy) {
	  if (task_item->fp == Hitcircle_Move) {
	    task_item->option.count = ONETIME_COUNT;
	  } else {
	    task_item->option.count = KILL_COUNT;
	  };
	};
      };
      list_item = list_item->next;
    };
    Kill_item(&enemy_list_header, (_list_item *)enemy->root);
    pfree(enemy->root);
  };
}

